<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>
</body>
<script id="ColorVS" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    uniform mat4 u_ModelM4;
    uniform mat4 u_PvM4;
    void main(){
        vec4 worldPosi = u_ModelM4 * a_Position;
        gl_Position = u_PvM4 * worldPosi;
    }

</script>
<script id="ColorFS" type="x-shader/x-fragment">
    precision mediump float;
    uniform vec4 u_Color;
    void main(){
        gl_FragColor = u_Color;
    }
</script>
<script id="StrokeVS" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    attribute vec3 a_Normal;
    uniform mat4 u_ModelM4;
    uniform mat4 u_PvM4;
    uniform float u_StrokeLen;
    varying vec3 v_OldPosi;
    varying vec3 v_NewPosi;
    void main(){
        vec4 worldPosi = u_ModelM4 * a_Position;
        vec3 normal = normalize(a_Normal);
        vec4 newWorldPosi = vec4(normal * u_StrokeLen, 0.0) + worldPosi;
        gl_Position = u_PvM4 * newWorldPosi;

        v_OldPosi = vec3(worldPosi);
        v_NewPosi = vec3(newWorldPosi);
    }
</script>
<script id="StrokeFS" type="x-shader/x-fragment">
    precision mediump float;
    uniform vec4 u_Color;
    varying vec3 v_OldPosi;
    varying vec3 v_NewPosi;
    void main(){
        float t = 0.0;
        float b = 0.0;
        float l = 0.0;
        float r = 0.0;
        if(v_NewPosi.y >= 0.3){
            t = 1.0 - distance(vec3(v_OldPosi.x, 0.3 ,0.0), v_NewPosi) / 0.1;
        }
        if(v_NewPosi.y <= -0.3){
            b = 1.0 - distance(vec3(v_OldPosi.x, -0.3 ,0.0), v_NewPosi) / 0.1;
        }
        if(v_NewPosi.x >= 0.3){
            r = 1.0 - distance(vec3(0.3, v_OldPosi.y ,0.0), v_NewPosi) / 0.1;
        }
        if(v_NewPosi.x <= -0.3){
            l = 1.0 - distance(vec3(-0.3, v_OldPosi.y ,0.0), v_NewPosi) / 0.1;
        }
        //gl_FragColor = vec4(u_Color.rgb, dist);
        float dist = t + b + r + l;
        gl_FragColor = vec4(vec3(dist), 1.0);
    }
</script>
<script type="module">
    import { Matrix4, Vector3, Color, OrthographicCamera, PerspectiveCamera, Spherical } from '../js/three.module.js';
    import { CreateProgram, imgPromise } from '../js/Utils.js';
    import lib from '../js/ThreeLib.js';
    import CameraController from '../js/CameraController.js'
    import Poly from '../js/Poly4.js'
    import Control from '../js/Control.js'
    import Square from '../js/objs/Square.js'
    import Sphere from '../js/objs/Sphere.js'

    import Geo from '../js/Poly2/Geo.js'
    import Mat from '../js/Poly2/Mat.js'
    import Obj3D from '../js/Poly2/Obj3D.js'
    import Scene from '../js/Poly2/Scene.js'
    import Frame from '../js/Poly2/Frame.js'

    //#region gl
    const canvas = document.getElementById("canvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    const gl = canvas.getContext("webgl", { antialias: true, });

    //gl.enable(gl.BLEND);
    //gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    //gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);//去图片描边
    //gl.enable(gl.DEPTH_TEST);
    //#endregion

    const scene = new Scene({ gl: gl });
    scene.addAxisProgram();
    scene.regProgram('color', {
        program: CreateProgram(gl,
            document.querySelector('#ColorVS').innerText,
            document.querySelector('#ColorFS').innerText),
        attribs: ['a_Normal', 'a_Position'],
        uniforms: ['u_PvM4', 'u_ModelM4', 'u_Color']
    });
    scene.regProgram('stroke', {
        program: CreateProgram(gl,
            document.querySelector('#StrokeVS').innerText,
            document.querySelector('#StrokeFS').innerText),
        attribs: ['a_Normal', 'a_Position'],
        uniforms: ['u_PvM4', 'u_ModelM4', 'u_Color', 'u_StrokeLen']
    });



    const pvM4 = new Matrix4();
    const modelM4 = new Matrix4();
    const eye = new Vector3(0, 0, 3);
    const lookAt = new Vector3();

    const camera = new PerspectiveCamera(45, canvas.width / canvas.height, 1, 20);
    const cameraController = new CameraController({
        canvas,
        camera,
        pvMatrix: pvM4,
        eye,
        lookAt,
    });


    const axis = new Obj3D().createAxis('axis');
    //scene.add(axis);

    const square = new Square(0.1);
    const [squareVertices, squareElements, squareNormals] = [
        square.vertices, square.elements, square.normals
    ];

    const sphere = new Sphere(0.1, 10, 10);
    const [sphereVertices, sphereElements, sphereNormals] = [
        sphere.vertices, sphere.elements, sphere.normals
    ];

    const w = 0.3;
    const h = 0.3;
    const [faceVertices, faceElements, faceNormals] = [
        [
            -w, h, 0,
            -w, -h, 0,
            w, -h, 0,
            w, h, 0,
        ], [
            0, 1, 2,
            0, 2, 3,
        ], [
            -w, h, 0,
            -w, -h, 0,
            w, -h, 0,
            w, h, 0,
        ]
    ];


    //main
    {
        //先插入, 不然描边会挡住本体
        scene.drawObjs.set('stroke', []);


        const face3D = create3D(faceVertices, faceElements);
        scene.add(face3D);
        const faceNormal = createNormal(0.1, faceVertices, faceNormals);
        //scene.add(faceNormal);
        const faceStroke = createStroke(face3D, 0.1, faceNormals);
        scene.add(faceStroke);

    }



    //检测鼠标是否进入四方体
    canvas.addEventListener('mousemove', function (e) {
        return;
        //当前指针在世界坐标的位置
        const { clientX, clientY } = e;
        const curr = getCurrWorldPosi(clientX, clientY);

        //记录选择的对象
        const inObj3D = [];
        for (let i = 0; i < spheres.length; i++) {
            //获取三角形集合
            const obj = spheres[i];
            const triangles = getTriangles(obj.geo.data['a_Position'].array, obj.geo.element.array);
            //检测是否在里面
            const m = isInTriangle(triangles, curr);
            if (m != null) {
                inObj3D.push({ obj: obj, m: m });
            }
        }
        //判断集合中哪个在最上面
        if (inObj3D.length > 0) {

            let curInd = 0;
            let m = inObj3D[curInd].m;
            let min = eye.clone().distanceTo(m);
            for (let i = 1; i < inObj3D.length; i++) {
                m = inObj3D[i].m;
                const dist = eye.clone().distanceTo(m);
                if (min > dist) {
                    min = dist;
                    curInd = i;
                }
            }
            let obj = inObj3D[curInd].obj;

            //获取到最近的对象
            //给这个对象套上描边
            const stroke = createStroke(obj);
            stroke.add(gl, obj);
            //关闭深度测试
            stroke.depthTest = false;

            //删除
            if (curStroke != null) {
                scene.remove(curStroke);
                scene.add(curStroke.children[0]);
            }
            curStroke = stroke;
            scene.remove(obj);
            scene.add(stroke);

        } else {
            if (curStroke != null) {
                scene.remove(curStroke);
                scene.add(curStroke.children[0]);
                curStroke = null;
            }
        }
    })

    function createStroke(obj3D, len, normals) {
        return new Obj3D({
            depthTest: false,
            geo: new Geo({
                data: {
                    'a_Position': obj3D.geo.data['a_Position'],
                    'a_Normal': { size: 3, array: new Float32Array(normals) }
                },
                element: obj3D.geo.element
            }),
            mat: new Mat({
                program: 'stroke',
                mode: ['TRIANGLES'],
                data: {
                    'u_PvM4': obj3D.mat.data['u_PvM4'],
                    'u_ModelM4': obj3D.mat.data['u_ModelM4'],
                    'u_Color': { type: 'uniform4fv', value: [0, 1, 1, 1] },
                    'u_StrokeLen': { type: 'uniform1f', value: len },
                },
            })
        });
    }

    function create3D(vertices, elements, color = [1, 1, 1, 1]) {
        return new Obj3D({
            geo: new Geo({
                data: {
                    'a_Position': { size: 3, array: new Float32Array(vertices) },
                },
                element: { array: new Uint16Array(elements) }
            }),
            mat: new Mat({
                program: 'color',
                mode: ['TRIANGLES'],
                data: {
                    'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                    'u_ModelM4': { type: 'uniformMatrix4fv', value: new Matrix4().elements },
                    'u_Color': { type: 'uniform4fv', value: color }
                },
            })
        });
    }

    function createNormal(len, vertices, normals, color = [1, 0, 0, 1]) {
        const normalLines = [];
        for (let i = 0; i < vertices.length; i += 3) {
            const curVertice = new Vector3(vertices[i], vertices[i + 1], vertices[i + 2]);
            const curNormal = new Vector3(normals[i], normals[i + 1], normals[i + 2]);
            normalLines.push(...curVertice, ...curNormal.setLength(len).add(curVertice));
        }
        return new Obj3D({
            geo: new Geo({
                data: {
                    'a_Position': { size: 3, array: new Float32Array(normalLines) },
                },
            }),
            mat: new Mat({
                program: 'color',
                mode: ['LINES'],
                data: {
                    'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                    'u_ModelM4': { type: 'uniformMatrix4fv', value: new Matrix4().elements },
                    'u_Color': { type: 'uniform4fv', value: color }
                },
            })
        });
    }

    function allTransform(vertices, mm4) {
        const verticesCur = [];
        for (let i = 0; i < vertices.length; i += 3) {
            const cur = new Vector3(vertices[i], vertices[i + 1], vertices[i + 2]);
            verticesCur.push(...cur.clone().applyMatrix4(mm4));
        }
        return verticesCur;
    }




    (function ani() {
        draw();
        requestAnimationFrame(ani)
    })();


    function draw() {
        scene.setUniform('u_PvM4', { value: pvM4.elements });

        scene.draw();
    }
</script>

</html>